package cn.lingyangwl.agile.infra.weixin.mp.handler.scanbiz;

import cn.lingyangwl.agile.infra.common.QrCodeSceneEnum;
import cn.lingyangwl.agile.infra.common.ScanScene;
import cn.lingyangwl.agile.model.constants.ValidationCons;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.Assert;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 扫码事件处理
 * @author shenguangyang
 */
@Slf4j
public abstract class BaseWxScanEventHandler {
    /**
     * 用于标识扫码票据参数的分割符
     */
    public static final String SEPARATOR = ":";
    private final static Map<QrCodeSceneEnum, BaseWxScanEventHandler> processorMap = new ConcurrentHashMap<>();

    @PostConstruct
    public void initScanEventProcessor() {
        processorMap.put(this.scene(), this);
    }

    /**
     * 处理扫码事件
     */
    public static void handle(WxMpXmlMessage message, String ticket) {
        if (StringUtils.isEmpty(ticket)) {
            return;
        }
        String[] ticketParams = ticket.split(SEPARATOR);
        if (ticketParams.length < 1) {
            log.error("非法 ticket: {}", ticket);
            return;
        }
        String sceneCode = ticketParams[0];
        QrCodeSceneEnum sceneEnum = QrCodeSceneEnum.of(sceneCode);
        if (sceneEnum == QrCodeSceneEnum.DEFAULT) {
            return;
        }

        if (sceneEnum.getParamKeys().size() != ticketParams.length) {
            log.error("非法 ticket: {}", ticket);
            throw new BizException("参数数量校验失败");
        }

        BaseWxScanEventHandler processor = processorMap.get(sceneEnum);
        if (Objects.isNull(processor)) {
            return;
        }

        ScanScene scanScene = new ScanScene();
        scanScene.setSceneCode(processor.scene().getCode());
        scanScene.setParams(processor.parseQrCodeTicket(ticketParams));
        processor.processBiz(message, scanScene);
        log.info("process scan event [{}]", JSON.toJSONString(scanScene));
    }

    public static BaseWxScanEventHandler getInstance(QrCodeSceneEnum sceneEnum) {
        BaseWxScanEventHandler processor = processorMap.get(sceneEnum);
        if (Objects.isNull(processor)) {
            return processorMap.get(QrCodeSceneEnum.DEFAULT);
        }
        return processor;
    }

    /**
     * 获取二维码票据, 用于标识二维码
     */
    public String getQrCodeTicket(ScanScene req) {
        JSONObject extParams = Optional.ofNullable(req.getParams()).orElse(new JSONObject());
        QrCodeSceneEnum scene = scene();

        List<String> params = new LinkedList<>();
        for (String paramKey : scene.getParamKeys()) {
            if (ScanScene.SCENE_CODE.equals(paramKey)) {
                params.add(scene.getCode());
                continue;
            }
            String value = extParams.getString(paramKey);
            Assert.notEmpty(value, paramKey + ValidationCons.NOT_EMPTY);
            params.add(value);
        }
        return String.join(SEPARATOR, params);
    }

    /**
     * 解析参数
     * @param ticketParams 票据参数
     */
    private JSONObject parseQrCodeTicket(String[] ticketParams) {
        JSONObject resp = new JSONObject();
        QrCodeSceneEnum scene = scene();
        List<String> paramKeys = scene.getParamKeys();
        for (int i = 1; i < paramKeys.size(); i++) {
            String key = paramKeys.get(i);
            String value = ticketParams[i];
            resp.put(key, value);
        }
        return resp;
    }

    protected abstract void processBiz(WxMpXmlMessage message, ScanScene scanScene);

    protected abstract QrCodeSceneEnum scene();
}
